package com.gsoe.auth.service.impl;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.gsoe.auth.utils.EmailUtils;
import com.gsoe.auth.utils.PasswordUtils;
import com.gsoe.auth.dto.*;
import com.gsoe.auth.mapper.PowerMapper;
import com.gsoe.auth.mapper.UserInfoMapper;
import com.gsoe.auth.mapper.UserProblemCollectMapper;
import com.gsoe.auth.mapper.UserRankingMapper;
import com.gsoe.auth.po.*;
import com.gsoe.auth.service.*;
import com.gsoe.auth.vo.*;
import com.gsoe.base.exception.UserException;
import com.gsoe.base.model.PageResult;
import com.gsoe.base.model.Result;
import com.gsoe.base.model.User;
import com.gsoe.base.utils.UserUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, User> implements UserInfoService {
    @Autowired
    StringRedisTemplate stringRedisTemplate;


    @Autowired
    DefaultKaptcha kaptcha;

    @Autowired
    UserInfoMapper userInfoMapper;

    @Autowired
    UserRoleService userRoleService;

    @Autowired
    UserRankingMapper userRankingMapper;

    @Autowired
    RoleService roleService;

    @Autowired
    RolePowerService rolePowerService;

    @Autowired
    PowerMapper powerMapper;

    @Autowired
    UserProblemCollectMapper userProblemCollectMapper;

    @Autowired
    EmailUtils emailUtils;

    @Override
    public UserInfoVO getUserInfo(Long id) {
        User user = getById(id);
        if(user == null){throw new UserException("user not found");}
        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtils.copyProperties(user,userInfoVO);
        return userInfoVO;
    }

    @Override
    public User doLogin(UserLoginDTO userLoginDTO) {
        String s = stringRedisTemplate.opsForValue().get("captcha:"+userLoginDTO.getId());
        if(StringUtils.isEmpty(s) || !s.equals(userLoginDTO.getCode())){
            throw new UserException("验证码错误或者验证已过期");
        }
        User user = getOne(new QueryWrapper<User>()
                .eq("user_name", userLoginDTO.getUsername())
                .or().eq("email", userLoginDTO.getUsername())
        );
        if(user == null){
            throw new UserException("未找到该用户");
        }
        if(user.getDisableTime()!=null && user.getDisableTime().isAfter(LocalDateTime.now())){
            throw new UserException("该账号已被禁封 禁封原因:"+ user.getDisableReason()+" 解封时间："+user.getDisableTime().toString());
        }

        boolean check=false;
        try {
            check=PasswordUtils.decryptAndCheck(userLoginDTO.getPassword(), user.getPassword());
        }catch (Exception e){
            throw new UserException("解密失败，登录失败");
        }

        if(!check){
            String failCountString = stringRedisTemplate.opsForValue().get("failCount:" + user.getId());
            int failCount=1;
            if(StringUtils.isNotEmpty(failCountString)){
                failCount = Integer.parseInt(failCountString)+1;
            }
            if(failCount >= 10){
                user.setDisableReason("尝试次数过多");
                user.setDisableTime(LocalDateTime.now().plusMinutes(1));
                updateById(user);
                StpUtil.disable(user.getId(),60);
                stringRedisTemplate.delete("failCount:" + user.getId());
                throw new UserException("账号或密码错误");
            }
            stringRedisTemplate.opsForValue().set("failCount:"+user.getId(),String.valueOf(failCount),1,TimeUnit.DAYS);
            throw new UserException("账号或密码错误");
        }
        return user;
    }

    @Override
    public CodeVO getCodeImage() {
        // 生成验证码文本和图片
        String captchaText = kaptcha.createText();
        String uuid = UUID.randomUUID().toString().substring(0,10);
        stringRedisTemplate.opsForValue().set("captcha:"+uuid, captchaText,120, TimeUnit.SECONDS);
        BufferedImage captchaImage = kaptcha.createImage(captchaText);

        // 将图片写入字节数组输出流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ImageIO.write(captchaImage, "png", baos);
        }catch (Exception e){
            throw new UserException(e.getMessage());
        }
        // 将字节数组转换为Base64字符串
        byte[] imageBytes = baos.toByteArray();
        String base64String =  "data:image/png;base64," + Base64.getEncoder().encodeToString(imageBytes);
        CodeVO codeVO =new CodeVO();
        codeVO.setBase64String(base64String);
        codeVO.setId(uuid);
        return codeVO;
    }

    @Override
    public List<String> getPowerListByRoleName(String roleName) {
        List<String> powerList=userInfoMapper.getPowerListByRoleName(roleName);
        return powerList.isEmpty()?List.of():powerList;
    }

    @Override
    public List<String> getRoleList(Long id) {
        List<String> roleList=userInfoMapper.getRoleList(id);
        return roleList.isEmpty()?List.of():roleList;
    }

    @Override
    public void doRegister(UserRegisterDTO userRegisterDTO) {
        String email = userRegisterDTO.getEmail();
        String code = userRegisterDTO.getCode();
        if(StringUtils.isNotEmpty(email) && (StringUtils.isEmpty(code))){
            throw new UserException("参数不合法，请检查后重试");
        }
        long cnt=count(new QueryWrapper<User>().eq("user_name", userRegisterDTO.getUsername()));
        if(cnt>0){
            throw new UserException("用户已存在");
        }
        if(StringUtils.isNotEmpty(email)){
            String emailCode = stringRedisTemplate.opsForValue().get("emailCode:" + email);
            if(StringUtils.isEmpty(emailCode) || !emailCode.equals(userRegisterDTO.getCode())){
                throw new UserException("邮箱验证码错误或者已经过期");
            }
        }

        User user=new User();
        user.setEmail(email);
        user.setPassword(PasswordUtils.encrypt(userRegisterDTO.getPassword()));
        user.setRealName(userRegisterDTO.getRealname());
        user.setUserName(userRegisterDTO.getUsername());

        save(user);
    }

    @Override
    public PageResult<UserVO> getUserList(UserDTO userDTO) {
        Page<User>page=new Page<>(userDTO.getPageNo(),userDTO.getPageSize());
        page=page(page,new QueryWrapper<User>()
                .eq(userDTO.getUserId()!=null,"id",userDTO.getUserId())
                .nested(StringUtils.isNotEmpty(userDTO.getUserName()),userQueryWrapper -> {
                    userQueryWrapper
                            .like("user_name",userDTO.getUserName())
                            .or()
                            .like("real_name",userDTO.getUserName());
                })

        );
        return PageResult.of(page,user -> {
            UserVO userVO=new UserVO();
            BeanUtils.copyProperties(user,userVO);
            userVO.setIsDisable(false);
            if(user.getDisableTime()!=null && LocalDateTime.now().isBefore(user.getDisableTime())){
                userVO.setIsDisable(true);
                userVO.setDisableReason(user.getDisableReason());
            }
            userVO.setRoles(getRoleList(user.getId()));
            return userVO;
        });
    }

    @Override
    @Transactional
    public void delUser(Long id) {
        if(id==1)throw new UserException("管理用户不可删");
        removeById(id);
        userRoleService.remove(new QueryWrapper<UserRole>().eq("user_id",id));
        userRankingMapper.delete(new QueryWrapper<UserRanking>().eq("user_id",id));
        StpUtil.kickout(id);
    }

    @Override
    public Result<UserRoleVO> getRoleListByid(Long id) {

        List<Long> userRoleIds = userRoleService.list(new QueryWrapper<UserRole>().eq("user_id", id))
                .stream().map(UserRole::getRoleId).toList();
        List<Role> allRoleList = roleService.list();

        UserRoleVO userRoleVO=new UserRoleVO();
        if(CollectionUtil.isEmpty(userRoleIds)){

            userRoleVO.setUserRoleList(new ArrayList<>());
            userRoleVO.setRaminRoleList(allRoleList);
            return Result.success(userRoleVO);
        }

        List<Role> userRoleList = roleService.list(new QueryWrapper<Role>()
                .in("id", userRoleIds)
        );

        List<Role> raminRoleList=new ArrayList<>();
        for (Role role : allRoleList) {
            if(!userRoleList.contains(role)){
                raminRoleList.add(role);
            }
        }

        userRoleVO.setRaminRoleList(raminRoleList);
        userRoleVO.setUserRoleList(userRoleList);
        return Result.success(userRoleVO);
    }

    @Override
    public void saveRoleList(UserRoleDTO dto) {
        Long userId = dto.getUserId();
        userRoleService.remove(new QueryWrapper<UserRole>().eq("user_id", userId));
        List<UserRole> list = dto.getChooseroleList().stream().map(role -> {
            UserRole userRole = new UserRole();
            userRole.setRoleId(role.getId());
            userRole.setUserId(userId);
            return userRole;
        }).toList();
        userRoleService.saveBatch(list);
        SaManager.getSaTokenDao().deleteObject("satoken:loginId-find-role:" + userId);
    }

    @Override
    public void userDisable(UserDisableDTO userDisableDTO) {
        Long userId = userDisableDTO.getUserId();
        if(userId==1L)throw new UserException("该账号无法禁封");
        User user = getById(userId);
        if(user==null){throw new UserException("无此id记录");}
        user.setDisableTime(LocalDateTime.now().plusDays(userDisableDTO.getD()).plusHours(userDisableDTO.getH()).plusMinutes(userDisableDTO.getM()));
        user.setDisableReason(userDisableDTO.getReason());
        boolean b = updateById(user);
        if(b){
            StpUtil.kickout(userId);
        }
    }

    @Override
    public void saveUserPermission(UserPermissionDTO userPermissionDTO) {
        List<Long> permissionIDList = userPermissionDTO.getPermissionIDList();
        Long roleId = userPermissionDTO.getRoleId();
        String roleName = userPermissionDTO.getRoleName();

        rolePowerService.remove(new QueryWrapper<RolePower>().eq("role_id", roleId));
        List<RolePower> rolePowerList = permissionIDList.stream().map(l -> {
            RolePower rolePower = new RolePower();
            rolePower.setRoleId(roleId);
            rolePower.setPowerId(l);
            return rolePower;
        }).toList();

        if(CollectionUtil.isNotEmpty(rolePowerList))rolePowerService.saveBatch(rolePowerList);
        SaManager.getSaTokenDao().deleteObject("satoken:role-find-permission:"+roleName);
    }

    @Override
    public PageResult<UserProblemCollectVO> getUserProblemCollect(Integer pageNo, Integer pageSize) {
        Long userId = UserUtil.getLoginUser().getId();
        Page<UserProblemCollect> page=new Page<>(pageNo,pageSize);
        page = userProblemCollectMapper.selectPage(page, new QueryWrapper<UserProblemCollect>().eq("user_id", userId));

        return PageResult.of(page,userProblemCollect -> {
            UserProblemCollectVO userProblemCollectVO=new UserProblemCollectVO();
            BeanUtils.copyProperties(userProblemCollect,userProblemCollectVO);
            return userProblemCollectVO;
        });
    }

    @Override
    public void addUserProblemCollect(UserProblemCollectDTO userProblemCollectDTO) {
        Long userId = UserUtil.getLoginUser().getId();
        userProblemCollectDTO.setUserId(userId);
        Long problemId = userProblemCollectDTO.getProblemId();
        userInfoMapper.deleteUserProblemCollect(problemId,userId);
        userInfoMapper.addUserProblemCollect(userProblemCollectDTO);
    }

    @Override
    public void deleteUserProblemCollect(Long problemId) {
        Long userId = UserUtil.getLoginUser().getId();
        userInfoMapper.deleteUserProblemCollect(problemId,userId);
    }

    @Override
    public List<Long> getUserAllProblemCollect(Long id) {
        return userProblemCollectMapper.selectList(new QueryWrapper<UserProblemCollect>().eq("user_id", id))
                .stream().map(UserProblemCollect::getProblemId).toList();
    }

    @Override
    public void updateUserInfo(UpdateUserDTO updateUserDTO) {
        User user=UserUtil.getLoginUser();
        BeanUtils.copyProperties(updateUserDTO,user);
        try {
            if(StringUtils.isNotEmpty(updateUserDTO.getPassword()))user.setPassword(PasswordUtils.encrypt(updateUserDTO.getPassword()));
        }catch (Exception e){
            throw new UserException("加密失败");
        }
        updateById(user);
    }

    @Override
    public void getEmailCode(String email) {
        String code = kaptcha.createText();
        if(Boolean.TRUE.equals(stringRedisTemplate.hasKey("emailCode:" + email)) && stringRedisTemplate.getExpire("emailCode:"+email)>0){
            throw new UserException("邮件已发送，请稍后重试");
        }
        stringRedisTemplate.opsForValue().set("emailCode:"+email,code,180,TimeUnit.SECONDS);
        emailUtils.sendToEmail(email,"【验证码】欢迎你注册浙江广厦OE系统，您的验证码为："+code+"。验证码三分钟内有效，请勿外传","广厦大学OE系统YYDS");
    }
}
